﻿using Modbus.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SunseaneModbusTool
{
    public partial class Form1 : Form
    {
        /// <summary>
        /// 私有串口实例
        /// </summary>
        private SerialPort serialPort = new SerialPort();

        /// <summary>
        /// 私有ModbusRTU主站字段
        /// </summary>
        private static IModbusMaster master;

        private string sAddressType = string.Empty; //0X,1X,3X,4X
        private decimal iSlaveId = 1; //站号
        private decimal iStartAdr = 0; //起始地址
        private decimal iAdrLength = 0;//地址长度

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //设置可选串口
            cbxPort.Items.AddRange(SerialPort.GetPortNames());
            //设置可选波特率
            cbxBaudRate.Items.AddRange(new object[] { 9600, 19200 });
            //设置可选奇偶校验
            cbxParity.Items.AddRange(new object[] { "None", "Odd", "Even", "Mark", "Space" });
            //设置可选数据位
            cbxDataBits.Items.AddRange(new object[] { 5, 6, 7, 8 });
            //设置可选停止位
            cbxStopBits.Items.AddRange(new object[] { 1, 1.5, 2 });
            //设置读写模式
            cbxMode.Items.AddRange(new object[] {
                "读取输出线圈0X",
                "读取离散输入1X",
                "读取输入寄存器3X",
                "读取保持型寄存器4X",
                //"写入单个线圈",
                //"写入多个线圈",
                //"写入单个寄存器",
                //"写入多个寄存器"
            });

            //设置默认选中项
            cbxPort.SelectedIndex = 1;
            cbxBaudRate.SelectedIndex = 0;
            cbxParity.SelectedIndex = 0;
            cbxDataBits.SelectedIndex = 3;
            cbxStopBits.SelectedIndex = 0;
            cbxMode.SelectedIndex = 0;

            nudLength.Minimum = 1;
            nudSlaveID.Minimum = 1;
            nudStartAdr.Minimum = 0;

            //设置为默认输入法，即为英文半角
            rbxRWMsg.ImeMode = ImeMode.Disable;
        }

        /// <summary>
        /// 模式切换事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbxMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            rbxRWMsg.Clear();
            if (cbxMode.SelectedItem.ToString().Contains("写入"))
            {                
                btnRW.Text = "写入";
                rbxRWMsg.Enabled = true;
                nudLength.Enabled = true;
            }
            else
            {
                btnRW.Text = "读取";
                rbxRWMsg.Enabled = true;
                nudLength.Enabled = true;
            }
        }

        /// <summary>
        /// 读写事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRW_Click(object sender, EventArgs e)
        {
            try
            {
                btnRW.Enabled = false;

                try
                {
                    //设定串口参数
                    //serialPort.PortName = cbxPort.SelectedItem.ToString();
                    serialPort.PortName = cbxPort.Text;
                    serialPort.BaudRate = (int)cbxBaudRate.SelectedItem;
                    serialPort.Parity = GetSelectedParity();
                    serialPort.DataBits = (int)cbxDataBits.SelectedItem;
                    serialPort.StopBits = GetSelectedStopBits();

                    //创建ModbusRTU主站实例
                    master = ModbusSerialMaster.CreateRtu(serialPort);
                    master.Transport.ReadTimeout = 1000;
                    //其中transport还有重试次数和重试间隔时间，默认是3和250
                    //master.Transport.Retries + "  " + master.Transport.WaitToRetryMilliseconds;

                    //打开串口
                    if (!serialPort.IsOpen) serialPort.Open();
                }
                catch(Exception portEx)
                {
                    MessageBox.Show(portEx.Message);
                    return;
                }

                iSlaveId = nudSlaveID.Value;
                iStartAdr = nudStartAdr.Value;
                iAdrLength = nudLength.Value;
                if (iStartAdr + iAdrLength > 65535)
                {
                    MessageBox.Show("读取地址不能超过65535");
                    return;
                }
                decimal iSlaveId2 = nudSlaveID2.Value;
                if (iSlaveId2 < iSlaveId)
                {
                    iSlaveId2 = iSlaveId;
                }
                for (int i = (int)iSlaveId; i <= iSlaveId2; i++)
                {
                    iSlaveId = i;
                    if (iAdrLength <= 120)
                    {
                        TodoRW();
                    }
                    else
                    {
                        decimal iAdrLengthTmp = iAdrLength;
                        iAdrLength = 100;
                        for (int i2 = (int)iStartAdr; i2 < iAdrLengthTmp; i2+=100)
                        {
                            iStartAdr = i2;
                            if (iStartAdr + iAdrLength > iAdrLengthTmp)
                            {
                                iAdrLength = iAdrLengthTmp - iStartAdr;
                            }
                            TodoRW();
                        }
                    }
                }                
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                btnRW.Enabled = true;
                if (serialPort.IsOpen) serialPort.Close();
            }
        }

        /// <summary>
        /// 更新写入值计数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rbxRWMsg_TextChanged(object sender, EventArgs e)
        {
            //iAdrLength = Regex.Matches(rbxRWMsg.Text, ",").Count + 1;
        }

        /// <summary>
        /// 获取窗体选中的奇偶校验
        /// </summary>
        /// <returns></returns>
        private Parity GetSelectedParity()
        {
            switch (cbxParity.SelectedItem.ToString())
            {
                case "Odd":
                    return Parity.Odd;
                case "Even":
                    return Parity.Even;
                case "Mark":
                    return Parity.Mark;
                case "Space":
                    return Parity.Space;
                case "None":
                default:
                    return Parity.None;
            }
        }

        /// <summary>
        /// 获取窗体选中的停止位
        /// </summary>
        /// <returns></returns>
        private StopBits GetSelectedStopBits()
        {
            switch (Convert.ToDouble(cbxStopBits.SelectedItem))
            {
                case 1:
                    return StopBits.One;
                case 1.5:
                    return StopBits.OnePointFive;
                case 2:
                    return StopBits.Two;
                default:
                    return StopBits.One;
            }
        }

        /// <summary>
        /// 写入单个线圈
        /// </summary>
        private void WriteSingleCoil()
        {
            bool result = false;
            if (rbxRWMsg.Text.Equals("true", StringComparison.OrdinalIgnoreCase) || rbxRWMsg.Text.Equals("1", StringComparison.OrdinalIgnoreCase))
            {
                result = true;
            }
            master.WriteSingleCoil((byte)iSlaveId, (ushort)iStartAdr, result);
        }

        /// <summary>
        /// 批量写入线圈
        /// </summary>
        private void WriteArrayCoil()
        {
            List<string> strList = rbxRWMsg.Text.Split(',').ToList();

            List<bool> result = new List<bool>();

            strList.ForEach(m => result.Add(m.Equals("true", StringComparison.OrdinalIgnoreCase) || m.Equals("1", StringComparison.OrdinalIgnoreCase)));

            master.WriteMultipleCoils((byte)iSlaveId, (ushort)iStartAdr, result.ToArray());
        }

        /// <summary>
        /// 写入单个寄存器
        /// </summary>
        private void WriteSingleRegister()
        {
            ushort result = Convert.ToUInt16(rbxRWMsg.Text);

            master.WriteSingleRegister((byte)iSlaveId, (ushort)iStartAdr, result);
        }

        /// <summary>
        /// 批量写入寄存器
        /// </summary>
        private void WriteArrayRegister()
        {
            List<string> strList = rbxRWMsg.Text.Split(',').ToList();

            List<ushort> result = new List<ushort>();

            strList.ForEach(m => result.Add(Convert.ToUInt16(m)));

            master.WriteMultipleRegisters((byte)iSlaveId, (ushort)iStartAdr, result.ToArray());
        }

        /// <summary>
        /// 读取输出线圈
        /// </summary>
        /// <returns></returns>
        private bool[] ReadCoils()
        {
            return master.ReadCoils((byte)iSlaveId, (ushort)iStartAdr, (ushort)iAdrLength);
        }

        /// <summary>
        /// 读取输入线圈
        /// </summary>
        /// <returns></returns>
        private bool[] ReadInputs()
        {
            return master.ReadInputs((byte)iSlaveId, (ushort)iStartAdr, (ushort)iAdrLength);
        }

        /// <summary>
        /// 读取保持型寄存器
        /// </summary>
        /// <returns></returns>
        private ushort[] ReadHoldingRegisters()
        {
            return master.ReadHoldingRegisters((byte)iSlaveId, (ushort)iStartAdr, (ushort)iAdrLength);
        }

        /// <summary>
        /// 读取输入寄存器
        /// </summary>
        /// <returns></returns>
        private ushort[] ReadInputRegisters()
        {
            return master.ReadInputRegisters((byte)iSlaveId, (ushort)iStartAdr, (ushort)iAdrLength);
        }

        /// <summary>
        /// 界面显示读取结果
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="result"></param>
        private void SetMsg<T>(List<T> result)
        {
            string msg = string.Empty;
            ushort inx = (ushort)iStartAdr;

            result.ForEach(m =>
            {
                msg += $"从站:{iSlaveId},地址:{sAddressType}{inx} = {m};\r\n";
                inx++;
            });

            rbxRWMsg.Text += msg;
            //rbxRWMsg.Text += "\r\n-----------\r\n";
        }

        private void TodoRW()
        {
            try
            {
                //根据选择的模式进行读写
                switch (cbxMode.SelectedItem.ToString())
                {
                    case "读取输出线圈0X":
                        try
                        {
                            sAddressType = "0X";
                            SetMsg(ReadCoils().ToList());
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                            string msg =  $"从站:{iSlaveId},地址:{sAddressType}{iStartAdr} = -;\r\n";
                            rbxRWMsg.Text += msg;
                        }
                        break;
                    case "读取离散输入1X":
                        sAddressType = "1X";
                        SetMsg(ReadInputs().ToList());
                        break;
                    case "读取保持型寄存器4X":
                        sAddressType = "4X";
                        SetMsg(ReadHoldingRegisters().ToList());
                        break;
                    case "读取输入寄存器3X":
                        sAddressType = "3X";
                        SetMsg(ReadInputRegisters().ToList());
                        break;
                    case "写入单个线圈":
                        if (rbxRWMsg.Text.Contains(","))
                        {
                            MessageBox.Show("输入值过多");
                            serialPort.Close();
                            return;
                        }
                        WriteSingleCoil();
                        break;
                    case "写入多个线圈":
                        WriteArrayCoil();
                        break;
                    case "写入单个寄存器":
                        if (rbxRWMsg.Text.Contains(","))
                        {
                            MessageBox.Show("输入值过多");
                            serialPort.Close();
                            return;
                        }
                        WriteSingleRegister();
                        break;
                    case "写入多个寄存器":
                        WriteArrayRegister();
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                string msg = $"从站:{iSlaveId},地址:{sAddressType}{iStartAdr} = -;\r\n";
                rbxRWMsg.Text += msg;
                //MessageBox.Show(ex.Message);
                Console.WriteLine(ex.Message);
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            rbxRWMsg.Clear();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            cbxPort.Items.Clear();
            cbxPort.Items.AddRange(SerialPort.GetPortNames());
        }
    }
}
